<?php
/**
 * Standard Deposit Form
 */
class DepositForm extends Form {
    private static $allowed_actions = array('httpSubmission');
    /**
     * Constructor
     *
     * @param Controller $controller The parent controller, necessary to
     *                               create the appropriate form action tag.
     * @param string $name The method on the controller that will return this
     *                     form object.
     * @param FieldList|FormField $fields All of the fields in the form - a
     *                                   {@link FieldList} of {@link FormField}
     *                                   objects.
     * @param FieldList|FormAction $actions All of the action buttons in the
     *                                     form - a {@link FieldList} of
     */
    function __construct($controller, $name, $fields = null, $actions = null, $validator = null) {
        if(!$fields) {
            if(Session::get('deposit_amount')){
                $fields = FieldList::create(
                	$balance_field = ReadonlyField::create('ShowPurchaseAccountBalance', _t('DepositForm.CURRENT_BALANCE', 'Current Balance'), Account::get('PurchaseAccount', $controller->CurrentMember()->ID)->obj('Balance')->Nice()),
                    $amount_field = ReadonlyField::create('ShowDepositAmount', _t('DepositForm.DEPOSIT_AMOUNT', 'Deposit Amount'), DBField::create_field('Currency', Session::get('deposit_amount'))->Nice())
                );
				$balance_field->setIncludeHiddenField(true);
				$amount_field->setIncludeHiddenField(true);
                $supported_methods = Payment::get_supported_methods();
                if(sizeof($supported_methods)){
                    $payment_methods = Config::inst()->get('DepositSubmission', 'payment_methods');
                    foreach($supported_methods as $method => $title){
                        if(!isset($payment_methods[$method]) || !$payment_methods[$method]){
                            Payment::remove_supported_methods($method);
                        }
                    }
                }
                $fields = Payment::combined_form_fields($fields, $controller->CurrentMember()->ID, Session::get('deposit_amount'));
            }
            else{
                $fields = singleton('DepositSubmission')->getDepositFormFields($controller->CurrentMember()->ID);
            }
        }
        
        if(!$actions) {
            if(Session::get('deposit_amount')){
                $actions = FieldList::create(
                    FormAction::create("doBack", _t('DepositForm.BACK', 'Back')),
                    FormAction::create("doConfirmPayment", _t('DepositForm.BUTTONCONFIRMPAYMENT', 'Confirm Payment'))
                );
            }
            else{
                $actions = FieldList::create(
                    FormAction::create("doProceedPayment", _t('DepositForm.BUTTONPROCEEDPAYMENT', 'Proceed to Payment'))
                );
            }
        }
        
        if(!$validator){
            if(Session::get('deposit_amount')){
                $validator = Payment_Validator::create();
            }
            else{
                $validator = RequiredFields::create('Amount');
            }
        }

        parent::__construct($controller, $name, $fields, $actions, $validator);
    }

    function doProceedPayment(array $data, $form) {
        Session::set('deposit_amount', $data['Amount']);
        return $this->controller->redirectBack();
    }
    
    function doBack(array $data, $form) {
        Session::clear('deposit_amount');
        return $this->controller->redirectBack();
    }

    function doConfirmPayment(array $data, $form) {
        try {
            DB::getConn()->transactionStart();
            $deposit_submission = DepositSubmission::create_deposit_submission($data, $this->controller->CurrentMember()->ID);
            $receipt = $deposit_submission->PaymentReceipt()->update($data)->setField('MemberID', $deposit_submission->MemberID)->updateTransactionType('DEPOSIT');
			$receipt->write();
            $payment = $receipt->addPayment($data['Payment'], $data);
            $deposit_submission->setField('PaymentReceiptID', $receipt->ID)->write();
            DB::getConn()->transactionEnd();
            Session::clear('deposit_amount');
            return $this->controller->redirect($payment->ProcessLink());
        }
        catch(ValidationException $e) {
            DB::getConn()->transactionRollback();
            SS_Log::log(new Exception(print_r($e->getMessage(), true)), SS_Log::NOTICE);
            $form->sessionMessage($e->getMessage(), 'bad');
        }
        return $this->controller->redirectBack();
    }

	public function httpSubmission($request) {
		// Strict method check
		if($this->strictFormMethodCheck) {
			
			// Throws an error if the method is bad...
			if($this->formMethod != strtolower($request->httpMethod())) {
				$response = Controller::curr()->getResponse();
				$response->addHeader('Allow', $this->formMethod);
				$this->httpError(405, _t("Form.BAD_METHOD", "This form requires a ".$this->formMethod." submission"));
			}

			// ...and only uses the vairables corresponding to that method type
			$vars = $this->formMethod == 'get' ? $request->getVars() : $request->postVars();
		} else {
			$vars = $request->requestVars();
		}
		
		// Populate the form
		$this->loadDataFrom($vars, true);
	
		// Protection against CSRF attacks
		$token = $this->getSecurityToken();
		if( ! $token->checkRequest($request)) {
			if (empty($vars['SecurityID'])) {
				$this->httpError(400, _t("Form.CSRF_FAILED_MESSAGE",
					"There seems to have been a technical problem. Please click the back button, 
					refresh your browser, and try again."));
			} else {
				Session::set("FormInfo.{$this->FormName()}.data", $this->getData());
				Session::set("FormInfo.{$this->FormName()}.errors", array());
				$this->sessionMessage(
					_t("Form.CSRF_EXPIRED_MESSAGE", "Your session has expired. Please re-submit the form."),
					"warning"
				);
				return $this->controller->redirectBack();
			}
		}
		
		// Determine the action button clicked
		$funcName = null;
		foreach($vars as $paramName => $paramVal) {
			if(substr($paramName,0,7) == 'action_') {
				// Break off querystring arguments included in the action
				if(strpos($paramName,'?') !== false) {
					list($paramName, $paramVars) = explode('?', $paramName, 2);
					$newRequestParams = array();
					parse_str($paramVars, $newRequestParams);
					$vars = array_merge((array)$vars, (array)$newRequestParams);
				}
				
				// Cleanup action_, _x and _y from image fields
				$funcName = preg_replace(array('/^action_/','/_x$|_y$/'),'',$paramName);
				break;
			}
		}
		
		// If the action wasnt' set, choose the default on the form.
		if(!isset($funcName) && $defaultAction = $this->defaultAction()){
			$funcName = $defaultAction->actionName();
		}
			
		if(isset($funcName)) {
			Form::set_current_action($funcName);
			$this->setButtonClicked($funcName);
		}

		// Permission checks (first on controller, then falling back to form)
		if(
			// Ensure that the action is actually a button or method on the form,
			// and not just a method on the controller.
			$this->controller->hasMethod($funcName)
			&& !$this->controller->checkAccessAction($funcName)
			// If a button exists, allow it on the controller
			&& !$this->actions->dataFieldByName('action_' . $funcName)
		) {
			return $this->httpError(
				403, 
				sprintf('Action "%s" not allowed on controller (Class: %s)', $funcName, get_class($this->controller))
			);
		} elseif(
			$this->hasMethod($funcName)
			&& !$this->checkAccessAction($funcName)
			// No checks for button existence or $allowed_actions is performed -
			// all form methods are callable (e.g. the legacy "callfieldmethod()")
		) {
			return $this->httpError(
				403, 
				sprintf('Action "%s" not allowed on form (Name: "%s")', $funcName, $this->name)
			);
		}
		// TODO : Once we switch to a stricter policy regarding allowed_actions (meaning actions must be set
		// explicitly in allowed_actions in order to run)
		// Uncomment the following for checking security against running actions on form fields
		/* else {
			// Try to find a field that has the action, and allows it
			$fieldsHaveMethod = false;
			foreach ($this->Fields() as $field){
				if ($field->hasMethod($funcName) && $field->checkAccessAction($funcName)) {
					$fieldsHaveMethod = true;
				}
			}
			if (!$fieldsHaveMethod) {
				return $this->httpError(
					403, 
					sprintf('Action "%s" not allowed on any fields of form (Name: "%s")', $funcName, $this->Name())
				);
			}
		}*/
		
		// Validate the form
		if($funcName != 'doBack' && !$this->validate()) {
			return $this->getValidationErrorResponse();
		}
		
		// First, try a handler method on the controller (has been checked for allowed_actions above already)
		if($this->controller->hasMethod($funcName)) {
			return $this->controller->$funcName($vars, $this, $request);
		// Otherwise, try a handler method on the form object.
		} elseif($this->hasMethod($funcName)) {
			return $this->$funcName($vars, $this, $request);
		} elseif($field = $this->checkFieldsForAction($this->Fields(), $funcName)) {
			return $field->$funcName($vars, $this, $request);
		}
		
		return $this->httpError(404);
	}
}
?>